use anyhow::anyhow;
use anyhow::ensure;
use anyhow::Result;
use fudata::db::kline;
use fudata::db::FUHLC;
use fudata::model::kline::Kline;
use fudata::model::kline::Klt;
use fudata::model::security;
use surrealdb::sql::Thing;

use crate::helper::convert_from_fuda_klt_to_eastmoney_klt;
use crate::helper::SecurityKlineWrap;

///
///
/// code: e.g. "000001", "300529", "600519", "688063", "837046"
///
/// end : str, 结束日期， '20500101' ，表示 2050年1月1日
///
/// klt : int,
///    行情之间的时间间隔，可选示例如下
///    - ``5``      : 5 分钟
///    - ``15``     : 15分钟
///    - ``30``     : 30分钟
///    - ``60``     : 60分钟
///    - ``101``    : 日
///    - ``102``    : 周
///    - ``103``    : 月
///
/// fqt : int,
///    复权方式，可选示例如下
///    - ``0`` : 不复权
///    - ``1`` : 前复权
///    - ``2`` : 后复权
///
/// lmt : int,
///    一次性获取记录个数,从 end 结束日期倒数
///
/// 如果更关注价格的实时变动和交易机会，可以选择前复权；如果需要进行历史数据回测和计算收益率，那么后复权可能更为合适。
pub async fn update_stock_kline_from_eastmoney(
    security: &security::Security,
    end: &str,
    klt: &Klt,
    fqt: u16,
    lmt: u32
) -> Result<u32> {
    // If you prefer to return a Result instead of panicking, replace the assert! macro with a similar check using ensure! or an early return with an error:
    ensure!(
        &security.type_ == &security::SecurityType::STK,
        format!(
            "(update_stock_kline_from_eastmoney) invalid type_, only STK security: {:?}",
            security
        )
    );

    get_and_update_stock_kline_from_eastmoney(security, end, klt, fqt, lmt).await
}

/// 获取并更新股票日线数据
///
/// end : 获取数据的结束日期
///
/// klt : K线类型
///
/// fqt : 复权类型
///
/// lmt : 获取数据的最大条数
///
/// Return 更新的记录个数
///
pub async fn get_and_update_stock_kline_from_eastmoney(
    security: &security::Security,
    end: &str,
    klt: &Klt,
    fqt: u16,
    lmt: u32
) -> Result<u32> {
    // If you prefer to return a Result instead of panicking, replace the assert! macro with a similar check using ensure! or an early return with an error:
    ensure!(
        &security.type_ == &security::SecurityType::STK ||
            &security.type_ == &security::SecurityType::KZZ ||
            &security.type_ == &security::SecurityType::ETF ||
            &security.type_ == &security::SecurityType::IDX,
        format!(
            "(stock_kline_craw::get_and_update_stock_kline_from_eastmoney) invalid type_, only STK,KZZ,ETF,IDX security: {:?}",
            security
        )
    );

    let security_id = security.id.clone().unwrap().id.to_raw();

    let mut updates = 0u32;

    let secid = match security.market {
        security::Market::SH => format!("1.{}", security.symbol),
        security::Market::SZ => format!("0.{}", security.symbol),
        security::Market::BJ => format!("0.{}", security.symbol),
        security::Market::ZZ => format!("2.{}", security.symbol),
    };
    let eastmoney_klt = convert_from_fuda_klt_to_eastmoney_klt(klt)?;

    let kk = eastmoney::stock::stock_kline
        ::get_stock_kline(&secid, end, eastmoney_klt, fqt, lmt).await
        .map_err(|err| anyhow!(err.to_string()))?;

    if let Some(kl_data) = &kk.data {
        let mut klines = Vec::new();
        for ktick in &kl_data.kline_ticks() {
            let kline_craw = SecurityKlineWrap::from(ktick);
            let mut kline = kline_craw.kline;
            kline.klt = klt.clone();
            kline.security_id = security_id.clone();
            kline.id = Some(
                Thing::from((
                    "kline",
                    Kline::id_from(&security_id.clone(), &kline.klt, &kline.klts).as_str(),
                ))
            );
            kline.last_updated = Some(format!("{}", FUHLC.new_timestamp()));
            klines.push(kline);

            updates += 1u32;
        }
        let _updated = kline::upsert_all(&klines).await?;
    } else {
        eprintln!(
            "(stock_kline_craw::get_and_update_stock_kline_from_eastmoney): kline_ticks is None for security.id: {:?} end:={} klt={}",
            security.id,
            end,
            klt
        );
    }

    Ok(updates)
}

#[cfg(test)]
mod test {
    use fudata::{ db, model::{ kline::Klt, security } };

    use crate::stock::{ stock_kline_craw, stocks_craw };

    #[tokio::test]
    async fn test_update_stock_kline_from_eastmoney() {
        let _ = db::init().await.unwrap();

        let rs = stocks_craw::update_stock_list_page_from_eastmoney(1, 3).await;
        println!("(update_stock_list_from_eastmoney) rs={:?}", rs);

        let securities = db::security
            ::select(&None, Some(security::SecurityType::STK), None, None, 0, 3).await
            .unwrap();

        let klts = [Klt::M005, Klt::M015, Klt::M030, Klt::M060, Klt::DAY, Klt::WEEK, Klt::MONTH];

        for klt in &klts {
            for security in securities.iter() {
                let rs = stock_kline_craw::update_stock_kline_from_eastmoney(
                    &security,
                    "20300101",
                    klt,
                    1,
                    10000
                ).await;
                println!("rs={:?}", rs);
                assert_eq!(rs.is_ok(), true);
                // assert_eq!(rs.ok().unwrap().len(), 10)
            }
        }
    }
}
